import os
import re

import requests
import json
import logging
from jsonpath_ng import parse
from tenacity import retry, stop_after_attempt, wait_exponential
from config import Config



class RequestUtil:
    def __init__(self):
        self.config = Config()
        # 基础配置
        self.base_url = self.config.base_url  # 从配置读取
        self.timeout = self.config.default_timeout  # 默认超时时间

        # 会话实例
        self.session = requests.Session()

        # 日志配置
        self.logger = logging.getLogger(__name__)

        # 全局headers
        self.session.headers.update(self.config.global_headers)

    @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
    def _request(self, method, endpoint, **kwargs):
        """统一请求方法"""
        url = f"{self.base_url}{endpoint}"

        # 处理超时
        kwargs.setdefault("timeout", self.timeout)

        # 记录请求日志
        self.log_request(method, url, kwargs)

        try:
            response = self.session.request(method, url, **kwargs)
            # 记录响应日志
            self.log_response(response)
            return response
        except requests.exceptions.RequestException as e:
            self.logger.error(f"请求异常: {str(e)}")
            raise ConnectionError(f"网络请求失败: {str(e)}")

    def log_request(self, method, url, kwargs):
        """记录请求日志"""
        self.logger.info(f"请求方法: {method}")
        self.logger.info(f"请求URL: {url}")
        if 'headers' in kwargs:
            self.logger.debug(f"请求头: {kwargs['headers']}")
        if 'json' in kwargs:
            self.logger.debug(f"请求体: {json.dumps(kwargs['json'], indent=2)}")
        if 'params' in kwargs:
            self.logger.debug(f"请求参数: {kwargs['params']}")

    def log_response(self, response):
        """记录响应日志"""
        self.logger.info(f"响应状态: {response.status_code}")
        try:
            self.logger.debug(f"响应体: {json.dumps(response.json(), indent=2)}")
        except json.JSONDecodeError:
            self.logger.debug(f"响应体: {response.text[:500]}...")

    # 简化方法
    def get(self, endpoint, params=None, **kwargs):
        return self._request('GET', endpoint, params=params, **kwargs)

    def post(self, endpoint, json=None, **kwargs):
        return self._request('POST', endpoint, json=json, **kwargs)

    def put(self, endpoint, json=None, **kwargs):
        return self._request('PUT', endpoint, json=json, **kwargs)

    def delete(self, endpoint, **kwargs):
        return self._request('DELETE', endpoint, **kwargs)

    def request_with_check(self, method, endpoint, expected_status=200, **kwargs):
        """带状态检查的请求"""
        response = self._request(method, endpoint, **kwargs)

        # 状态码检查
        if response.status_code != expected_status:
            error_msg = (f"状态码异常! 预期: {expected_status}, "
                         f"实际: {response.status_code}, "
                         f"URL: {response.url}")
            self.logger.error(error_msg)
            raise AssertionError(error_msg)

        return response

    def get_json(self, method, endpoint, **kwargs):
        """获取JSON响应"""
        response = self._request(method, endpoint, **kwargs)
        try:
            return response.json()
        except json.JSONDecodeError:
            self.logger.error(f"JSON解析失败: {response.text[:200]}")
            raise ValueError("响应不是有效的JSON格式")

    def extract_by_jsonpath(self, response, jsonpath_expr, default=None):
        """使用JSONPath提取值"""
        expr = parse(jsonpath_expr)
        try:
            json_data = response.json()
            matches = [match.value for match in expr.find(json_data)]
            return matches[0] if matches else default
        except Exception as e:
            self.logger.error(f"JSONPath解析失败: {expr} | 错误: {str(e)}")
            return default

    @classmethod
    def extract_by_regex(cls, text, pattern, group=1, default=None):
        """使用正则表达式提取文本内容"""
        if isinstance(text, (dict, list)):
            text = json.dumps(text)

        match = re.search(pattern, text)
        if match and group <= len(match.groups()):
            return match.group(group)
        return default

    def multi_extract(self, response, extract_map):
        """
        批量提取数据
        :param response: 响应对象
        :param extract_map: 提取映射 {'字段名': ('类型', '表达式')}
        :return: 提取结果字典
        """
        results = {}
        for key, (extract_type, expr) in extract_map.items():
            if extract_type == 'jsonpath':
                results[key] = self.extract_by_jsonpath(response, expr)
            elif extract_type == 'regex':
                results[key] = self.extract_by_regex(response.text, expr)
        return results

    def save_response(self, response, file_path):
        """保存响应内容到文件"""
        os.makedirs(os.path.dirname(file_path), exist_ok=True)

        if 'application/json' in response.headers.get('Content-Type', ''):
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(response.json(), f, indent=2, ensure_ascii=False)
        else:
            with open(file_path, 'wb') as f:
                f.write(response.content)
        self.logger.info(f"响应已保存至: {file_path}")




if __name__ == '__main__':
    api_client = RequestUtil()
    user_data = {
        "name": "test",
        "email": "test@example.com",
        "password": "P@ssw0rd"
    }
    respond = api_client.post('users/', json=user_data)
    print(respond.json())

    user_data = api_client.get('users/123/')
    print(user_data.json())